package com.rankeiot.platform.controller;

import com.rankeiot.core.anno.Auth;
import com.rankeiot.core.data.response.Resp;
import com.rankeiot.core.util.RsaUtil;
import com.rankeiot.core.util.StringUtil;
import com.rankeiot.platform.PlatformMenu;
import com.rankeiot.platform.config.DataEaseConfig;
import com.rankeiot.platform.util.DefaultTokenManager;
import com.rankeiot.platform.util.TokenManager;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.SneakyThrows;
import org.noear.snack.ONode;
import org.springframework.http.*;
import org.springframework.util.MultiValueMap;
import org.springframework.util.StreamUtils;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.client.HttpClientErrorException;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.socket.CloseStatus;
import org.springframework.web.socket.WebSocketHandler;
import org.springframework.web.socket.WebSocketMessage;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.client.WebSocketClient;
import org.springframework.web.socket.client.standard.StandardWebSocketClient;

import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.net.URISyntaxException;
import java.security.Key;
import java.util.*;
import static com.rankeiot.core.util.LocaleUtil.t;
/**
 * DataEase集成
 */
@RestController
public class DataEaseController {
    private static final String prefix = "/ease/";
    private static final Set<String> directPaths = new HashSet<>(Arrays.asList("assets", "js"));
    private RestTemplate restTemplate = new RestTemplate();
    private TokenManager tokenManager=new DefaultTokenManager(this::updateToken);

    static final String rsaPublicKey="MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC1GQ3ySMJAjN9SdvbjCYA+VyTYr3DS5fe79mcbpf1AERWZUQchWUngQn0jslQqm/Y9Grp2sw5FAXglyg9eMDB1aitvbXQWEhzLLX9hdwx/c4+BoIqIHRWhjshB4Tr08VMEpZfT5TvOnqR7qa37UNpdKVLG7Mhcy2qflhgXHdUGpQIDAQAB";
    private final static Key rsaKey;

    static {
        try {
            rsaKey = RsaUtil.loadPublicKeyFromString(rsaPublicKey);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
    public static String getProxyHost() {
        return DataEaseConfig.HOST.strValue();
    }
    private static String encrypt(String target) throws Exception {
       return RsaUtil.encrypt(target,rsaKey);
    }

//    public static void main(String[] args) throws Exception {
//        // L%WilX(5!w
//        //link-qS3IKcKk
//        //{"c":1720075674502,"e":253402300799000,"v":"\"HKo8FpNSOgEzPGZfDmhmSTc2ClocYL41xosDOZ/Da2EUR6gMp6gg11zScWsLcJzGqlgbnzWbLklnuyBtLwXFoD19KXF+xLiiL9vR6VJq/M9443sFmyYjIXZiYjzDPy20c84XVWk/+dihtHgqB5Zqz4IksBRT+tsWLb/Rfsq7KYo=\""}
//        System.out.println(encrypt("qS3IKcKk,L%WilX(5!w"));
//    }
    @SneakyThrows
    public void updateToken(TokenManager.Token token){
        //获取新token
        if(token.getExpirationTime()==0){
           // Key  pk= RsaUtil.loadPublicKeyFromString(rsaPublicKey);
            String username=encrypt(DataEaseConfig.USERNAME.strValue());
            String pwd=encrypt(DataEaseConfig.PASSWORD.strValue());
            Map<String,String> params=new HashMap<>();
            params.put("name",username);
            params.put("pwd",pwd);
            String retStr= restTemplate.postForObject(getProxyHost()+"de2api/login/localLogin",params,String.class);
            ONode node= ONode.load(retStr);
            if(node.get("code").getInt()==0){
                String tokenStr=node.select("data.token").getString();
                token.setToken(tokenStr);
                long exp=node.select("data.exp").getLong();
                token.setExpirationTime(exp);
            }
        }else{
        //刷新token

        }
    }
    private void checkDataEase(){
        if(StringUtil.isEmpty(getProxyHost())){
            Resp.fail(t("DataEase未配置","DataEase not configured"),1000);
        }
    }
    @GetMapping("/ease/getToken")
    @Auth(PlatformMenu.DATAEASE_CONTROL)
    public Resp getToken(){
        checkDataEase();
        return Resp.of(tokenManager.getToken());
    }
    @Auth(PlatformMenu.DATAEASE)
    @GetMapping("/ease/boards")
    public Resp dataV(String busiFlag){
        checkDataEase();
        Map<String,String> body=new HashMap<>();
        body.put("busiFlag",busiFlag);
        HttpHeaders headers=new HttpHeaders();
        headers.add("Content-Type","application/json");
        headers.add("X-De-Token",tokenManager.getToken());
        HttpEntity entity=new HttpEntity(body,headers);
        Map ret=restTemplate.postForObject(getProxyHost()+"de2api/dataVisualization/tree",entity,Map.class);
        return Resp.of(ret.get("data"));
    }
    @Auth(PlatformMenu.DATAEASE)
    @GetMapping("/ease/shareDetail/{id}")
    public Resp shareDetail(@PathVariable String id) throws Exception {
        checkDataEase();
        HttpHeaders headers=new HttpHeaders();
        headers.add("Content-Type","application/json");
        headers.add("X-De-Token",tokenManager.getToken());
        HttpEntity entity=new HttpEntity(headers);
        Map ret= restTemplate.exchange(getProxyHost()+"de2api/share/detail/"+id,HttpMethod.GET,entity,Map.class).getBody();
        Map detailData=(Map)ret.get("data");

        String pwd=(String)detailData.get("pwd");
        if(StringUtil.isNotEmpty(pwd)){
            String uuid=(String)detailData.get("uuid");
            detailData.put("enc",encrypt(uuid+","+pwd));
        }
        return Resp.of(detailData);
    }
    @RequestMapping(value = {"/ease/de2api/**", "/ease/", "/ease/websocket/info", "/ease/websocket/*/*/xhr_streaming", "/ease/websocket/*/*/xhr_send"}, method = {RequestMethod.GET, RequestMethod.POST, RequestMethod.PUT, RequestMethod.DELETE}, produces = MediaType.TEXT_PLAIN_VALUE)
    public ResponseEntity proxyApi(HttpServletRequest request, HttpServletResponse response) {
        try {
            String redirectUrl = buildRedirectUrl(request);
            RequestEntity requestEntity = createRequestEntity(request, redirectUrl);
            return route(requestEntity);
        } catch (HttpClientErrorException e) {
            return new ResponseEntity(e.getStatusText(), e.getStatusCode());
        } catch (Exception e) {
            e.printStackTrace();
            return new ResponseEntity("REDIRECT ERROR", HttpStatus.INTERNAL_SERVER_ERROR);
        }
    }

    @RequestMapping(value = {"/ease/assets/**",
            "/ease/js/**",
            "/ease/dataease.svg",
            "ease/tinymce-dataease-private/**",
            "/ease/de2api/static-resource/**"}, method = {RequestMethod.GET, RequestMethod.POST}, produces = MediaType.TEXT_PLAIN_VALUE)
    public ResponseEntity proxyAssets(HttpServletRequest request, HttpServletResponse response) {
        String redirectUrl = buildRedirectUrl(request);
        HttpHeaders headers = new HttpHeaders();
        headers.add("Location", redirectUrl);
        return new ResponseEntity<>(headers, HttpStatus.SEE_OTHER);
    }

    private String buildRedirectUrl(HttpServletRequest request) {
        String queryString = request.getQueryString();
        String requestUri = request.getRequestURI().replace(prefix, "");
        return getProxyHost() + requestUri + (queryString != null ? "?" + queryString : "");
    }


    private RequestEntity createRequestEntity(HttpServletRequest request, String url) throws URISyntaxException, IOException {
        String method = request.getMethod();
        HttpMethod httpMethod = HttpMethod.valueOf(method);
        MultiValueMap<String, String> headers = parseRequestHeader(request);
        byte[] body = parseRequestBody(request);
        return new RequestEntity<>(body, headers, httpMethod, new URI(url));
    }


    private byte[] parseRequestBody(HttpServletRequest request) throws IOException {
        InputStream inputStream = request.getInputStream();
        return StreamUtils.copyToByteArray(inputStream);
    }

    private MultiValueMap<String, String> parseRequestHeader(HttpServletRequest request) {
        HttpHeaders headers = new HttpHeaders();
        List<String> headerNames = Collections.list(request.getHeaderNames());
        for (String headerName : headerNames) {
            if(headerName.equals("Host")){
               // System.out.println(headerName+":");
                continue;
            }
            List<String> headerValues = Collections.list(request.getHeaders(headerName));
            for (String headerValue : headerValues) {
                headers.add(headerName, headerValue);
            }
        }
        return headers;
    }

    private ResponseEntity<String> route(RequestEntity requestEntity) {

        return restTemplate.exchange(requestEntity, String.class);
    }


    private static class DataEaseWebSocketProxyHandler implements WebSocketHandler {
        private WebSocketSession frontend;
        private WebSocketSession backend;

        public DataEaseWebSocketProxyHandler(WebSocketSession session) {
            frontend = session;
        }

        public void sendMessageToBackend(WebSocketMessage<?> message) throws IOException {
            if (backend != null && backend.isOpen()) {
                backend.sendMessage(message);
            }
        }

        public void closeBackend(CloseStatus closeStatus) throws IOException {
            if (backend != null && backend.isOpen()) {
                backend.close(closeStatus);
            }
        }

        @Override
        public void afterConnectionEstablished(WebSocketSession session) throws Exception {
            backend = session;
        }

        @Override
        public void handleMessage(WebSocketSession session, WebSocketMessage<?> message) throws Exception {
            frontend.sendMessage(message);
        }

        @Override
        public void handleTransportError(WebSocketSession session, Throwable exception) throws Exception {

        }

        @Override
        public void afterConnectionClosed(WebSocketSession session, CloseStatus closeStatus) throws Exception {
            frontend.close(closeStatus);
        }

        @Override
        public boolean supportsPartialMessages() {
            return false;
        }
    }

    public static class DataEaseWebSocketHandler implements WebSocketHandler {
        private WebSocketClient webSocketClient = new StandardWebSocketClient();


        @Override
        public void afterConnectionEstablished(WebSocketSession session) throws Exception {
            // 连接到实际的WebSocket服务器
            URI uri = session.getUri();

            String url = session.getUri().toString();
            String socketHost = getProxyHost().replace("http", "ws");
            int idx = url.indexOf(prefix);
            String target = socketHost + url.substring(idx + prefix.length());
            WebSocketHandler proxyHandler = new DataEaseWebSocketProxyHandler(session);
            session.getAttributes().put("proxy", proxyHandler);

            webSocketClient.execute(proxyHandler, null, URI.create(target));
        }

        @Override
        public void handleMessage(WebSocketSession session, WebSocketMessage<?> message) throws Exception {
            // 转发消息到实际的WebSocket服务器
            //   webSocketSession.sendMessage(message);
            DataEaseWebSocketProxyHandler handler = (DataEaseWebSocketProxyHandler) session.getAttributes().get("proxy");
            handler.sendMessageToBackend(message);
        }

        @Override
        public void handleTransportError(WebSocketSession session, Throwable exception) throws Exception {


        }

        @Override
        public void afterConnectionClosed(WebSocketSession session, CloseStatus closeStatus) throws Exception {
            DataEaseWebSocketProxyHandler handler = (DataEaseWebSocketProxyHandler) session.getAttributes().get("proxy");
            handler.closeBackend(closeStatus);
        }

        @Override
        public boolean supportsPartialMessages() {
            return false;
        }
    }
}
